/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-  */
/*
 * net.c
 * Copyright (C) 2016 
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name ``'' nor the name of any other
 *    contributor may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * netdog IS PROVIDED BY  ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL  OR ANY OTHER CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include<string.h> 
#include<stdlib.h>
#include<sys/socket.h>
#include<errno.h>
#include<pthread.h>
#include<netdb.h>
#include<arpa/inet.h>
#include "netdog.h"

#define ALLWAYS 1
#define NONE 0
char* resolve_hostname(char *);
void  get_local_ip (char *buffer);
unsigned short checksum(unsigned short *ptr,int nbytes);
int send_syn(int fd,struct sockaddr_in dest,char *datagram);
int receive_ack(int fd);
int process_packet(struct sockaddr_in *wherefrom,unsigned char* buffer, int size);

int detect_port(char *target,int port,int flag)
{
    int fd = socket(AF_INET,SOCK_RAW,IPPROTO_TCP);
    if(fd < 0){
	printf ("Error creating socket. Error number : %d . \
	         Error message : %s \n" , errno , strerror(errno));
        exit(0);
    }
    //Datagram to represent the packet
    char datagram[40];
    //TCP Header
    struct tcphdr *tcph = (struct tcphdr *)datagram;
    struct sockaddr_in  dest;
    struct pseudo_header phdr;
    memset(&dest,0,sizeof(struct sockaddr_in));
    if(inet_addr(target) != -1)
    {
	dest.sin_addr.s_addr = inet_addr(target);
    }
    else
    {
	if(resolve_hostname(target) != NULL)
	{
	    dest.sin_addr.s_addr = inet_addr(resolve_hostname(target));
	}
	else
	{
	    printf("Unable to resolve hostname : %s",target);
	    exit(1);
	}
    }
    int src_port = 54321;
    memset(datagram,0,40);

    //Building TCP Header
    tcph->source = htons ( src_port );
    tcph->dest = htons (port);
    tcph->seq = htonl(1105024978);
    tcph->ack_seq = 0;
    tcph->doff = sizeof(struct tcphdr) / 4;      //Size of tcp header
    tcph->fin=0;
    tcph->syn=1;
    tcph->rst=0;
    tcph->psh=0;
    tcph->ack=0;
    tcph->urg=0;
    tcph->window = htons ( 14600 );
    tcph->check = 0; 
    tcph->urg_ptr = 0;

    dest.sin_family = AF_INET;
	//for(;;)
	//{
	//while(mycond == 0)
		//pthread_cond_wait(&thread_cond,&mutex);
	send_syn(fd,dest,(char*)datagram);
	//}
    close(fd);
    return 0;
}
int send_syn(int fd,struct sockaddr_in dest,char *datagram)
{
    struct tcphdr * tcph = (struct tcphdr *)datagram;
    struct pseudo_header phdr;
    char src_ip[20];
    get_local_ip(src_ip);
    tcph->check = 0;
    phdr.src_addr = inet_addr(src_ip);
    phdr.dst_addr = dest.sin_addr.s_addr;
    phdr.placeholder = 0;
    phdr.protocol = IPPROTO_TCP;
    phdr.tcp_length = htons( sizeof(struct tcphdr) );
    memcpy(&phdr.tcp , tcph , sizeof (struct tcphdr));
    tcph->check = checksum( (unsigned short*) &phdr , sizeof (struct pseudo_header));

    //Send the packet
    if (sendto (fd, datagram , sizeof(struct tcphdr) , 0 , (struct sockaddr *) &dest, sizeof (dest)) < 0)
    {
	printf ("Error sending syn packet. Error number : %d . \
		 Error message : %s \n" , errno , strerror(errno));
        return -1;
    }
	receive_ack(fd);
    return 0;
}
/*
 * Success,return 0
 */
int receive_ack(int fd)
{
    int saddr_size , data_size;
    struct sockaddr saddr;
    unsigned char *buffer = (unsigned char *)malloc(80); 

    saddr_size = sizeof saddr;
    //Receive a packet
    while(1)
    {
	//Why use while,because recvfrom grab all packets here.
        data_size = recvfrom(fd, buffer , 80 , 0 , &saddr , &saddr_size);
        if(data_size <0 )
        {
        printf("Recvfrom error , failed to get packets\n");
        exit(1);
        }
        //Now process the packet
        if(!process_packet((struct sockaddr_in *)&saddr,buffer , data_size))
	    break;//quit loop,if Success
    }
    printf("opened\n");
    free(buffer);
    return 0;
}

int process_packet(struct sockaddr_in *wherefrom,unsigned char* buffer, int size)
{
    //Get the IP Header part of this packet
    struct iphdr *iph = (struct iphdr*)buffer;
    struct sockaddr_in source;
 //   unsigned short iphdrlen;
    if(iph->protocol == 6)
    {
        struct iphdr *iph = (struct iphdr *)buffer;
  //      iphdrlen = iph->ihl*4;
        struct tcphdr *tcph=(struct tcphdr*)(buffer + (iph->ihl<<2));
        memset(&source, 0, sizeof(source));
        source.sin_addr.s_addr = iph->saddr;
        if(tcph->syn == 1 && tcph->ack == 1 && \
	   source.sin_addr.s_addr == wherefrom->sin_addr.s_addr) 
	    return 0; //Successed
    }
    return 1;//Failed
}


/*
 * Get ip from domain name
 */
char* resolve_hostname(char * hostname)
{
    struct hostent *he;
    struct in_addr **addr_list;
    int i;

    if ( (he = gethostbyname( hostname ) ) == NULL)
    {
        // get the host info
        herror("gethostbyname");
        return NULL;
    }

    addr_list = (struct in_addr **) he->h_addr_list;

    for(i = 0; addr_list[i] != NULL; i++)
    {
        //Return the first one;
        return inet_ntoa(*addr_list[i]) ;
    }

    return NULL;
}

/*
 Get source IP of system , like 192.168.0.6 or 192.168.1.2
 */

void  get_local_ip (char *buffer)
{
    //char buffer[20];
    int dns_port = 53;
    const char* DnsIp = "8.8.8.8";
    struct sockaddr_in serv;
    struct sockaddr_in name;
    int sock = socket ( AF_INET, SOCK_DGRAM, 0);

    memset( &serv, 0, sizeof(serv) );
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(DnsIp);
    serv.sin_port = htons( dns_port );

    int err = connect( sock , (const struct sockaddr*) &serv , sizeof(serv) );

    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (struct sockaddr*) &name, &namelen);

    const char *p = inet_ntop(AF_INET, &name.sin_addr, buffer, 100);
    close(sock);
}

unsigned short checksum(unsigned short *ptr,int nbytes)
{
    register long sum;
    unsigned short oddbyte;
    register short answer;

    sum=0;
    while(nbytes>1) {
        sum+=*ptr++;
        nbytes-=2;
    }
    if(nbytes==1) {
        oddbyte=0;
        *((u_char*)&oddbyte)=*(u_char*)ptr;
        sum+=oddbyte;
    }

    sum = (sum>>16)+(sum & 0xffff);
    sum = sum + (sum>>16);
    answer=(short)~sum;

    return(answer);
}
